﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Com.PhilChuang.Utils;

namespace Com.PhilChuang.Mobile.Apps.LocationSender.Model.Data
{
	/// <summary>
	/// Adapted from System.Device.Location.GeoCoordinate. Had to basically recreate it so that it's platform agnostic.
	/// </summary>
	[Serializable]
	public class GeoCoordinate : ICloneable
	{
		[SimpleEqualsAndHashcode]
		public double Altitude { get; set; }

		[SimpleEqualsAndHashcode]
		public double Course { get; set; }

		[SimpleEqualsAndHashcode]
		public double HorizontalAccuracy { get; set; }

		[SimpleEqualsAndHashcode]
		public bool IsUnknown { get; set; }

		[SimpleEqualsAndHashcode]
		public double Latitude { get; set; }

		[SimpleEqualsAndHashcode]
		public double Longitude { get; set; }

		[SimpleEqualsAndHashcode]
		public double Speed { get; set; }

		[SimpleEqualsAndHashcode]
		public double VerticalAccuracy { get; set; }

		[SimpleEqualsAndHashcode]
		public DateTime Timestamp { get; set; }

		public GeoCoordinate ()
		{
		}

		#region Equals / Clone
		public override bool Equals (object obj)
		{ return SimpleEqualsAndHashcode.Equals (this, obj); }

		public override int GetHashCode ()
		{ return SimpleEqualsAndHashcode.GetHashCode (this); }

		public virtual Object Clone ()
		{ return SimpleEqualsAndHashcode.Clone (this); }
		#endregion

		public override string ToString ()
		{
			return ToString (GeoCoordinateFormat.LatLongAlt);
		}

		public String ToString (GeoCoordinateFormat format)
		{
			switch (format)
			{
				case GeoCoordinateFormat.LatLong:
					return "{0:0.000000},{1:0.000000}".FormatWith (Latitude, Longitude);
				case GeoCoordinateFormat.LatLongAlt:
					return "{0:0.000000},{1:0.000000},{2:0.000}".FormatWith (Latitude, Longitude, Altitude);
				case GeoCoordinateFormat.DegreesMinutesSeconds:
					return "{0}{1} {2}{3}".FormatWith (
						ToDegMinSec (Latitude), Latitude > 0 ? "N" : "S",
						ToDegMinSec (Longitude), Longitude > 0 ? "E" : "W");
				case GeoCoordinateFormat.GoogleMapsUrl:
					return "http://maps.google.com/maps?q=Here+I+Am@{0}".FormatWith (ToString (GeoCoordinateFormat.LatLong));
				case GeoCoordinateFormat.BingMapsUrl:
					return "http://bing.com/maps/?v=2&where1={0}&q={0}".FormatWith (ToString (GeoCoordinateFormat.LatLong));
				case GeoCoordinateFormat.BingMapsMobileUrl:
					return "http://m.bing.com/search/search.aspx?l={0}&a=modifylocation&Q=map".FormatWith (ToString (GeoCoordinateFormat.LatLong));
				case GeoCoordinateFormat.Wp7MapsUrl:
					return "maps:{0}".FormatWith (ToString (GeoCoordinateFormat.LatLong));
				default: 
					return "Unhandled format";
			}
		}

		private static String ToDegMinSec (double decDeg)
		{
			var deg = decDeg >= 0 ? Math.Floor (decDeg) : Math.Ceiling (decDeg);
			var temp = Math.Abs (decDeg - deg) * 60;
			var min = Math.Floor (temp);
			var sec = (temp - min) * 60;
			if (sec >= 60d)
			{
				min++;
				sec -= 60d;
			}
			if (min >= 60d)
			{
				deg++;
				min -= 60d;
			}

			return "{0:0}°{1:0}'{2:0.0000}\"".FormatWith (deg, min, sec);
		}
	}
}
